﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using SKYPE4COMLib;

namespace SkypeWishmaster
{
	class Program
	{
		private static readonly Random _random = new Random();
		private static HashSet<string> _chatNames;
		private static string[] _topics;
		private static int _currentTopicIndex;
		private static long _topicRefreshInterval;
		private static Tuple<string[], string[]>[] _autoReplyes;
		private static bool _isPaused;
		private static readonly Timer _pauseTimer = new Timer(PauseTimerTick);

		private static void Main(string[] args)
		{
			var configPath = args.Any() ? args[0] : "config.xml";
			try
			{
				LoadConfig(configPath);
			}
			catch (Exception exception)
			{
				Console.WriteLine("Ощибка при загрузе настроек из '{0}': {1}", configPath, exception.Message);
				return;
			}

			var skype = new Skype();

			((_ISkypeEvents_Event)skype).AttachmentStatus +=
				status =>
				{
					Console.WriteLine("Соедиенеие со Skype: " + skype.Convert.AttachmentStatusToText(status));
					if (status == TAttachmentStatus.apiAttachAvailable)
						skype.Attach();
				};

			skype.MessageStatus += SkypeOnMessageStatus;

			if (!skype.Client.IsRunning)
				skype.Client.Start();

			if (skype.CurrentUserStatus == TUserStatus.cusOffline)
				skype.ChangeUserStatus(TUserStatus.cusOnline);

			if (_chatNames.Any())
				foreach (var chatName in _chatNames)
					Console.WriteLine("Следим за конференцией '{0}'.", chatName);
			else
				Console.WriteLine("Следим за всеми конфернциями.");

			var topicRefreshTimer =
				_topics.Any() && _chatNames.Any()
					? new Timer(state => SetNextTopic(skype), null, 0, _topicRefreshInterval)
					: null;

			Console.ReadKey(true);

			_pauseTimer.Dispose();
			if (topicRefreshTimer != null)
				topicRefreshTimer.Dispose();
		}

		private static void SkypeOnMessageStatus(ChatMessage message, TChatMessageStatus status)
		{
			if (_chatNames.Any() && !_chatNames.Contains(message.ChatName))
				return;

			if (status != TChatMessageStatus.cmsReceived)
				return;

			switch (message.Type)
			{
				case TChatMessageType.cmeSaid:
					TryAutoReply(message);
					Pause();
					break;
				case TChatMessageType.cmeSetTopic:
					TrySetTopic(message.Chat);
					Pause();
					break;
			}
		}

		private static void TryAutoReply(IChatMessage message)
		{
			if (_isPaused)
				return;

			foreach (var substringsAndResponses in _autoReplyes)
				foreach (var substring in substringsAndResponses.Item1)
					if (message.Body.IndexOf(substring, StringComparison.CurrentCultureIgnoreCase) > -1)
					{
						Console.WriteLine(
							"[{0}] Автоответ пользователю '{1}' на подстроку '{2}'.",
							DateTime.Now,
							message.FromDisplayName,
							substring);

						try
						{
							message.Chat.SendMessage(
								substringsAndResponses.Item2[_random.Next(substringsAndResponses.Item2.Length)]);
						}
						catch (Exception exception)
						{
							Console.WriteLine("Ошибка при автоответе: " + exception.Message);
						}
					}
		}

		private static void TrySetTopic(IChat chat)
		{
			if (_isPaused || _currentTopicIndex < 0)
				return;

			var currentSubject = _topics[_currentTopicIndex];

			if (chat.Topic == currentSubject)
				return;

			try
			{
				chat.Topic = currentSubject;

				Console.WriteLine(
					"[{0}] Тема конференции '{1}' сменена на '{2}'.",
					DateTime.Now,
					chat.Name,
					currentSubject);
			}
			catch (Exception exception)
			{
				Console.WriteLine("Ошибка при установке темы конференции {0}: {1}", chat.Name, exception.Message);
			}
		}

		private static void SetNextTopic(ISkype skype)
		{
			if (!_topics.Any() || !_chatNames.Any())
			{
				_currentTopicIndex = -1;
				return;
			}

			if (_currentTopicIndex < _topics.Length - 1)
				_currentTopicIndex++;
			else
				_currentTopicIndex = 0;

			foreach (var chatName in _chatNames)
				TrySetTopic(skype.Chat[chatName]);
		}

		private static void Pause()
		{
			_pauseTimer.Change(500, 0);
			_isPaused = true;
		}

		private static void PauseTimerTick(object state)
		{
			_isPaused = false;
		}

		private static void LoadConfig(string path)
		{
			const string schemaResource = "SkypeWishmaster.ConfigDescriptor.xsd";
			const string schemaUri = "http://skype-wishmaster.googlecode.com/hg/SkypePlugin/ConfigDescriptor.xsd";

			var xmlReaderSettings = new XmlReaderSettings { ValidationType = ValidationType.Schema };
			xmlReaderSettings.Schemas.Add(
				// ReSharper disable AssignNullToNotNullAttribute
				XmlSchema.Read(typeof(Program).Assembly.GetManifestResourceStream(schemaResource),
				// ReSharper restore AssignNullToNotNullAttribute
				null));
			using (var xmlReader = XmlReader.Create(path, xmlReaderSettings))
			{
				var xmlDocument = XDocument.Load(xmlReader);
				var xmlRoot = xmlDocument.Element(XName.Get("config", schemaUri));

				// ReSharper disable PossibleNullReferenceException
				var chatNamesElement = xmlRoot.Element(XName.Get("chatNames", schemaUri));
				// ReSharper restore PossibleNullReferenceException
				var protectedSubjectsElenent = xmlRoot.Element(XName.Get("topics", schemaUri));
				var autoResponseElement = xmlRoot.Element(XName.Get("autoResponses", schemaUri));

				_chatNames =
					chatNamesElement != null
						? new HashSet<string>(chatNamesElement.Elements().Select(chatName => (string)chatName))
						: new HashSet<string>();

				if (protectedSubjectsElenent != null)
				{
					var intervalAttribute = protectedSubjectsElenent.Attribute("interval");
					_topicRefreshInterval = (long)intervalAttribute;
					_topics = protectedSubjectsElenent.Elements().Select(subject => (string)subject).ToArray();
				}
				else
					_topics = new string[0];

				_currentTopicIndex = -1;
				_autoReplyes =
					autoResponseElement != null
						? autoResponseElement
							.Elements(XName.Get("item", schemaUri))
							.Select(
								item =>
								Tuple.Create(
									item
										.Elements(XName.Get("substring", schemaUri))
										.Select(substring => (string)substring)
										.ToArray(),
									item
										.Elements(XName.Get("response", schemaUri))
										.Select(response => (string)response)
										.ToArray()))
							.ToArray()
						: new Tuple<string[], string[]>[0];
			}
		}
	}
}